#ifndef _COMPILATION_TASK8PROPAGATOR_H_
#define _COMPILATION_TASK8PROPAGATOR_H_

#include <compilation/instruction/Instruction.h>
#include <compilation/task/Task.h>


namespace compilation
{

namespace detail
{

inline bool basic_propagation(
    VirtualInstruction* instr,
    VirtualTask& task,
    WorkListReusability& reuse)
{
  int nexts = instr->countNexts();
  bool result = false;

  if (!nexts) result = true;

  if (instr->getSNextInstruction())
  {
    if (!instr->getSNextInstruction()->mark)
    {
      task.clearInstruction();
      task.setInstruction(*instr->getSNextInstruction());
      reuse.setReuse();
    }

    result = (nexts == 1);
  }

  return result;
}

} // detail

template <InstructionType _Type, typename _TaskType, typename _AgendaType>
struct InstructionPropagator
{
  typedef InstructionPropagator base_propagator;

  bool operator ()(
      VirtualInstruction* instr,
      _TaskType& task,
      _AgendaType& continuations,
      WorkListReusability& reuse)
  { return detail::basic_propagation(instr,task,reuse); }
};

template <typename _TaskType, typename _AgendaType>
struct InstructionPropagator<TGoto, _TaskType, _AgendaType>
{
  typedef InstructionPropagator base_propagator;

  bool operator ()(
      VirtualInstruction* instr,
      _TaskType& task,
      _AgendaType& continuations,
      WorkListReusability& reuse)
  {
    bool hasResult = detail::basic_propagation(instr, task, reuse);

    GotoInstruction* gt = instruction_cast<TGoto>(instr);
    if (gt->isContext(GotoInstruction::CLoop) ||
        gt->isContext(GotoInstruction::CBeforeLabel) ||
        gt->isContext(GotoInstruction::CUndefined))
      reuse.setSorted();
    return hasResult;
  }
};

template <typename _TaskType, typename _AgendaType>
struct InstructionPropagator<TIf, _TaskType, _AgendaType>
{
  typedef InstructionPropagator base_propagator;

  bool operator ()(
      VirtualInstruction* instr,
      _TaskType& task,
      _AgendaType& continuations,
      WorkListReusability& reuse)
  {
    bool hasResult = detail::basic_propagation(instr, task, reuse);

    if (!hasResult)
    {
      IfInstruction* ifi = instruction_cast<TIf>(instr);

      if (ifi->getThen())
      {
        if (!ifi->getThen()->mark)
        {
          DEBUG_INSTRUCTION_VISITOR
              << "clone task for then"
              << std::endl;

          std::auto_ptr<VirtualTask> newTask(task.clone());
          newTask->clearInstruction();
          newTask->setInstruction(*ifi->getThen());
          continuations.addNewSorted(newTask.release());
          reuse.setSorted();
        }
      }
    }

    return true;
  }
};

template <typename _TaskType, typename _AgendaType>
struct DefaultInstructionPropagator
{
  template <InstructionType _Type>
  struct type : public InstructionPropagator<_Type, _TaskType, _AgendaType> {};
};

} // compilation

#endif // _COMPILATION_TASK8PROPAGATOR_H_
